Skip to content

read_group_chat bypasses the join-floor clamp, so a Bot's floor above seq reads a full room as empty - #2791

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-read-group-floor-clamp
Nov 10, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-read-group-floor-clamp

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

read_group_chat bypasses the join-floor clamp, so a Bot's floor above seq reads a full room as empty

Fixes #2790

Owner: Dev-1
Written-by: Dev-1
Bot: Dev-1
Reviewer: Birfy
Risk tier: T3
Base: 74d08c5
Claim-Id: 4bc854af8bdd4889a31174abc5395f83

What changed

read_group_chat resolved its floor from the Bot's own participant record and filtered room.log on the raw joinedSeq, so the #2754 clamp — which the room reads of a human actor already run — never applied to a Bot's read. A stored floor above the room's own room.seq (the shape a host counter left on a participant record before #2612/#2613 stopped the write) therefore hid every line from that reader and answered "<room>" has no messages yet in a room the pane and every other reader still showed.

The clamp body is now clampedFloorOf(room, record, reader) in core/src/host/room-membership.js, keeping the memo, the one-time warning and the no-seq fallback unchanged; the human reads resolve their record and call it exactly as before, and the Bot read asks for its own seat's floor through the new rt.seatFloor(room, seat). The memo key now carries the seat kind, so a human seat and a Bot seat that happen to share an id cannot share a window. core/src/host/groups.js uses it and keeps botPart?.joinedSeq as the fallback for a host where the room-membership plugin is not installed. docs/subsystems/core-host.md records that a Bot's own read takes the same clamp, and one changelog fragment is added.

Red first — clean base 74d08c5d, only the new test on the branch

$ node --import ./test/harness-resolve.mjs --test test/room-read-floor-clamp.test.js
not ok 1 - a Bot reads a room whose stored join floor is above room.seq
    a room with lines reads as empty: "Ops" has no messages yet.
not ok 2 - the clamped window opens onto lines that arrive afterwards
not ok 5 - a room without seq clamps to its stored log
# tests 5  # pass 2  # fail 3

The two guards that already pass are the healthy-floor read and an empty room's own sentence, so the red is only the defect's face.

After the fix

$ node --import ./test/harness-resolve.mjs --test test/room-read-floor-clamp.test.js
# tests 5  # pass 5  # fail 0

$ node --import ./test/harness-resolve.mjs --test test/room-*.test.js test/group*.test.js test/face-*.test.js test/details-group-*.test.js test/mock-group-roster.test.js test/link-room-title.test.js
# 33 files: # tests 254  # pass 254  # fail 0

$ cd plugins/workflow && node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 518  # pass 518  # fail 0

Generated mirrors

node scripts/ds-bot-patches.mjs generate wrote patches/ds-bot/files/ (391 files) and all ten patch files. Against this base the change is exactly three generated paths:

  • patches/ds-bot/0012-room-membership.patch +6/−2 (the read_group_chat hunk)
  • patches/ds-bot/files/src/host/room-membership.js +10/−3
  • patches/ds-bot/files/test/room-read-floor-clamp.test.js (new, 111 lines)

check answers all three lines ok — ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5), 10 patches apply cleanly in order, core/ equals ds-bot/ + patches/ds-bot/ — and canonical reports patches/ds-bot/ is what generate writes (generate is a no-op).

Ran

  • TZ=UTC npm run verify — exit 0: core # tests 3190 / # pass 3190, social 619, tree 143, timeline 161, workflow 518, .github/scripts + policy 671, interop 37, live-e2e 34, zero failures.
  • node scripts/lint.mjs — Found 88 warnings and 0 errors (the warnings are on main too).
  • ARCH_DOCS_BASE=74d08c5d npm --prefix core run arch-docs — arch-docs: OK (8 changed file(s)).
  • node .github/scripts/changelog.mjs check — 20 fragment(s) in changelog.d/ are well formed.
  • node scripts/ds-bot-patches.mjs generate / check / canonical as above.
  • Verified commit 685c3210 (tree 5060cdd3), eight files +247/−10; the pushed head is generated from that tree by the push helper.

Not run

  • the smoke test: no index.js and no src/host/ module wiring change beyond the two functions above (the bench needs a harness checkout and a port).
  • npm run pack, typecheck and actionlint: not part of npm run verify.

Checkpoint

{
  "pr": 2791, "claim": "Dev-1", "issue": 2790,
  "summary": "read_group_chat filtered the room log on the Bot's raw joinedSeq, so the #2754 clamp the human reads already run never applied: a stored floor above room.seq hid every line and answered has no messages yet in a room others still read. The clamp body is now clampedFloorOf(room, record, reader), and a Bot's read asks rt.seatFloor for its own seat, so the memo, one-time warning and no-seq fallback stay one rule. core/test/room-read-floor-clamp.test.js covers the clamp, window healing, healthy floors, an empty room and a room saved before seq. generate, check and canonical are green.",
  "files": ["core/src/host/groups.js", "core/src/host/room-membership.js", "core/test/room-read-floor-clamp.test.js", "docs/subsystems/core-host.md", "changelog.d/2790-read-group-floor-clamp.fixed.md", "patches/ds-bot/0012-room-membership.patch", "patches/ds-bot/files/src/host/room-membership.js", "patches/ds-bot/files/test/room-read-floor-clamp.test.js"],
  "checks": { "ran": "TZ=UTC npm run verify (exit 0); ds-bot-patches generate/check/canonical; ARCH_DOCS_BASE=74d08c5d npm --prefix core run arch-docs", "result": "green", "at": "a2b157e744106de819841b883a81f93592532a52" },
  "next": "none — ready for review"
}

`read_group_chat` resolved its floor from the Bot's participant record and
filtered `room.log` on the raw `joinedSeq`, so the ThinkFlowLab#2754 clamp — which the
human read paths already use — never ran for a Bot's read. A stored floor
above the room's own `room.seq` hid every line from that reader and answered
`"<room>" has no messages yet` in a room everyone else still reads.

The clamp body is now `clampedFloorOf(room, record, reader)` and the read
asks for its seat's floor through `rt.seatFloor`, so the memo, the one-time
warning and the no-`seq` fallback are the same one rule. `core/test/
room-read-floor-clamp.test.js` covers the clamp, the window healing on the
next line, healthy floors, an empty room and a room saved before `seq`.
@HugoXK

HugoXK commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request for the current tip a2b157e744106de819841b883a81f93592532a52 (one request naming this tip; a verdict is only read as naming a2b157e74410).

What to judge at this tip, core/src/host/ plus the generated patches/ds-bot/ output:

  • read_group_chat no longer filters room.log on the raw joinedSeq: it asks for its own seat's floor through the new rt.seatFloor(room, seat), which runs the same clamp body #2754 gave the human reads — memo, one warn per reader, no-seq fallback unchanged. The memo key now carries the seat kind so a human seat and a Bot seat sharing an id cannot share a window. botPart?.joinedSeq stays as the fallback for a host without the room-membership plugin.
  • Red first is in the body: on a clean 74d08c5d with only the new test, # tests 5 / # pass 2 / # fail 3 (a room with lines reads as empty: "Ops" has no messages yet.); after the fix the file is 5/5 and 33 room/group/face files are 254/254.
  • The generated diff is exactly three paths (0012-room-membership.patch +6/−2, files/src/host/room-membership.js +10/−3, the new files/test/room-read-floor-clamp.test.js), check prints its three ok lines and canonical reports generate as a no-op. Eight files +247/−10 in total against base 74d08c5d.

@Birfy

Birfy commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at a2b157e
Written-by: Reviewer

No actionable findings at this tip. I read the diff itself (8 files, +247/−10) and re-derived the defect on the base instead of trusting the body.

The defect is real on base 74d08c5d. read_group_chat filtered room.log on the Bot's raw botPart?.joinedSeq (core/src/host/groups.js, base line 1045), while the human room reads resolve their record through floorOf in core/src/host/room-membership.js, the #2754 clamp (memo, one warn per room, no-seq reduce). A seat whose joinedSeq is 9 in a room whose seq is 5 keeps nothing and answers "Ops" has no messages yet. — reproduced below.

The fix applies the clamp, not a special case. clampedFloorOf(room, record, reader) is the old floorOf body with the record and the memo key passed in; floorOf(room, actor) still resolves the human record and key exactly as before, and rt.seatFloor(room, seat) keys a Bot seat as bot:<id>, so a human seat and a Bot seat sharing an id cannot share a window. groups.js uses rt.seatFloor?.(room, botPart) ?? botPart?.joinedSeq ?? 0, so a host without the room-membership plugin keeps the old raw fallback, and an absent botPart still yields 0. The memo, the one-time warn and the no-seq reduce are unchanged, and the legacy case clamps to the stored log's last seq, never 0.

Red first confirmed in a copy. The tip's new test dropped onto the base tree is red 3/5 with exactly the defect's face; the same file on the tip tree is 5/5.

Generated mirrors match the source. patches/ds-bot/files/src/host/room-membership.js and patches/ds-bot/files/test/room-read-floor-clamp.test.js are byte-identical to core/; groups.js has no files/ mirror (it is patch-only, as the generated output describes), and patches/ds-bot/0012-room-membership.patch carries the two comment lines plus the rt.seatFloor line with its hunk header bumped +539,14 → +539,16.

Checks at this tip: 18 runs, 15 success + 2 skipped + 1 failure; the only red is enqueue-guard's no Verifier report names the tip a2b157e74410, which this report answers. The head is a fork with maintainer_can_modify: true and no hold.

Ran:

  • DSH_HARNESS=<harness> TZ=UTC node --import ./test/harness-resolve.mjs --test test/room-read-floor-clamp.test.js in the tip tree (codeload tarball of a2b157e7) → pass, # tests 5 / # pass 5 / # fail 0
  • same command with the tip's test/room-read-floor-clamp.test.js copied onto the base tree 74d08c5d → fail, # tests 5 / # pass 2 / # fail 3 (not ok 1, not ok 2, not ok 5, each a room with lines reads as empty: "Ops" has no messages yet.)
  • the 33 room/group/face files named in the body, run one by one at the tip → 254/254 pass (room-membership 26, room-floor-once 3, groups 6, group-work 28, the new file 5, the rest unchanged)
  • node .github/scripts/changelog.mjs check → pass, 20 fragment(s) in changelog.d/ are well formed
  • DSH_HARNESS=<harness> node core/scripts/lint.mjs → Found 88 warnings and 0 errors
  • diff of the two generated files/ mirrors against core/ → identical; 0012-room-membership.patch read hunk by hunk against the core diff
  • risk tier computed with the repo's own createGate/judge over GET /pulls/2791/files → risk-tier: T3; basis: core/src/host/room-membership.js=T3, core/src/host/groups.js=T2, changelog.d/…=T1, core/test/room-read-floor-clamp.test.js=T1, …

Not run:

  • full npm run verify locally — the tip's ubuntu-latest · Node 24 · DSH 0.2.0-rc.2 job (flybot-tests) and tests-ok are green at this same sha; I ran the 33 affected files
  • node scripts/ds-bot-patches.mjs check / canonical — needs a git checkout with ds-bot/ (this box cannot reach github.com); CI's patch steps are green at this tip and the mirrors are byte-identical
  • arch-docs against the base — needs git to diff 74d08c5d; the tip's arch-docs step is green
  • smoke / Desktop / real-model steward — host-side read path only

This PASS names only the tip above; a new commit voids it, and it is not by itself a queue permit — enqueue-guard reads it at this sha.

@github-actions github-actions Bot added the pass label Nov 10, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 10, 2026
Merged via the queue into ThinkFlowLab:main with commit 2cd5c29 Nov 10, 2026
18 checks passed
@Birfy
Birfy deleted the dev/1-read-group-floor-clamp branch November 10, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

read_group_chat bypasses the join-floor clamp, so a Bot's floor above seq reads a full room as empty

2 participants